#region license
# This file is part of Grawlix.
# 
# Grawlix is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
# 
# Grawlix is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public
# License along with Grawlix.  If not, see
# <http://www.gnu.org/licenses/>.
#endregion

"""OpenGL texture loading and binding."""

namespace Grawlix.Services.Rendering
import Grawlix
import OpenTK.Graphics
import System

internal final class GLTextureState(ITextureState):
"""A texture object with specified state."""
	
	def constructor([required] image as Image):
	"""
	Initializes a new texture.
	Param image: The image to load.
	"""
		Load(image.ImageData)
	
	# TODO: Refactor texture dimensions later to be
	# 	more efficient using OpenGL extensions.
	# Could use proxy target.
	private def ValidDimension(dimension as int):
	"""
	Returns a valid dimension for GL textures.
	Param dimension: The dimension to check.
	Returns: A next best dimension to use.
	Remarks: Fix for non-power-of-2 or
		less than 64 or greater than 256.
	"""
		if dimension < 64:
			return 64
		if dimension > 256:
			return 256
		# Already a power of 2
		if (dimension & dimension - 1) == 0:
			return dimension
		nextBest = 128
		while nextBest < dimension:
			nextBest *= 2
		return nextBest
	
	private def Load(image as Drawing.Image):
	"""
	Load a new bitmap to this object.
	Param image: The image to load.
	Param inverseDimensions: The inverse dimensions of the bitmap.
	Remarks: The function assumes it has an OpenGL context.
	"""
		# Resize for GL
		width = ValidDimension(image.Width)
		height = ValidDimension(image.Height)
		bitmap as Drawing.Bitmap = image
		if bitmap is null or width != image.Width or height != image.Height:
		   	bitmap = Drawing.Bitmap(image, width, height)
		inverseDimensions = 1 / Vector(width, height)
		
		# Get id
		id as UInt32
		GL.GenTextures(1, id)
		GL.BindTexture(TextureTarget.Texture2D, id)
		
		# Load texture
		lockRect = Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height)
		mode = Drawing.Imaging.ImageLockMode.ReadOnly
		format = Drawing.Imaging.PixelFormat.Format32bppArgb
		data = bitmap.LockBits(lockRect, mode, format)
		GL.TexImage2D(
			TextureTarget.Texture2D, 0,
			PixelInternalFormat.Rgba,
			data.Width, data.Height, 0,
			PixelFormat.Bgra, PixelType.UnsignedByte,
			data.Scan0)
		bitmap.UnlockBits(data)
		
		# Texture parameters
		GL.TexParameter(
			TextureTarget.Texture2D,
			TextureParameterName.TextureMinFilter,
			cast(int, TextureMinFilter.Linear))
		GL.TexParameter(
			TextureTarget.Texture2D,
			TextureParameterName.TextureMagFilter,
			cast(int, TextureMagFilter.Linear))
		
		# Set state
		_inverseDimensions = inverseDimensions
		_id = id
	
	internal def Reset():
	"""Drop the texture without unloading it."""
		_id = 0
		_inverseDimensions = Vector(1, 1)
	
	internal def Unload():
	"""Unload all data taken up by this object."""
		if _disposed:
			raise ObjectDisposedException('GLTextureState')
		_disposed = true
		GL.DeleteTextures(1, _id)
		Reset()
	
	internal def Bind():
	"""Bind to this texture and set up the texture matrix."""
		if _disposed:
			raise ObjectDisposedException('GLTextureState')
		GL.BindTexture(TextureTarget.Texture2D, _id)
		GL.MatrixMode(MatrixMode.Texture)
		GL.LoadIdentity()
		GL.Scale(_inverseDimensions.X, _inverseDimensions.Y, 1)
	
	Dimensions:
	"""The the dimensions of the texture."""
		get:
			return 1 / _inverseDimensions
	
	[getter(InverseDimensions)]
	private _inverseDimensions = Vector(1, 1)
	"""The reciprocal of the dimensions of the texture."""
	
	private _id as uint
	"""The id of the texture state."""
	
	private _disposed as bool
	"""Whether this object has been disposed."""
	
